package org.elsfs.cloud.common.mybatis.ext.sql;


import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.FieldEnumResolverSupport;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.FromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.StandardFromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.SubQueryFromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.CompositeSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.ExpressionSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.StandardSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SubQuerySelectItem;
import org.elsfs.cloud.common.util.lang.ArrayUtils;
import org.elsfs.cloud.common.util.lang.CollectionUtils;
import org.elsfs.cloud.common.util.lang.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;

/**
 * 抽象查询SQL实现
 *
 * @param <C> 子类具体类型
 */
@SuppressWarnings("unchecked")
public abstract class AbstractSelectSql<C extends AbstractSelectSql<C>> extends AbstractSelectJoin<C> implements SelectSql<C, AbstractSelectSql<C>.SelectConditionBuilderImpl, AbstractSelectCondition<AbstractSelectSql<C>.SelectConditionBuilderImpl>.HavingConditionBuilderImpl> {

    /**
     * 子类实现
     */
    protected final C childThis = (C) this;
    /**
     * 选择项列表
     */
    private final List<SelectItem> selectItems = Lists.newArrayList();
    /**
     * From项
     */
    private FromItem fromItem;
    /**
     * 查询条件构建器
     */
    private SelectConditionBuilderImpl selectConditionBuilder;

    /**
     * 解析字段枚举名称
     *
     * @param field 字段枚举
     * @return 字段名称
     */
    protected String resolveFieldName(Enum<?> field) {
        return FieldEnumResolverSupport.resolveFieldName(
            this.getTableAliasManager(),
            field
        );
    }

    /**
     * 选择SQL字段
     *
     * @param fields SQL字段
     * @return 具体实现
     */
    @Override
    public C select(String... fields) {
        if (ArrayUtils.isEmpty(fields)) {
            return this.childThis;
        }

        for (String field : fields) {
            this.selectItems.add(
                new StandardSelectItem(field)
            );
        }

        return this.childThis;
    }

    /**
     * 选择SQL字段表达式
     *
     * @param expressions SQL字段表达式
     * @return 具体实现
     */
    @Override
    public C selectExpression(String... expressions) {
        if (ArrayUtils.isEmpty(expressions)) {
            return this.childThis;
        }

        for (String expression : expressions) {
            this.selectItems.add(
                new ExpressionSelectItem(expression)
            );
        }

        return this.childThis;
    }

    /**
     * 选择SQL字段
     *
     * @param fields SQL字段枚举
     * @return 具体实现
     */
    @Override
    public C select(Enum<?>... fields) {
        if (ArrayUtils.isEmpty(fields)) {
            return this.childThis;
        }

        for (Enum<?> field : fields) {
            this.selectItems.add(
                new StandardSelectItem(
                    this.resolveFieldName(field)
                )
            );
        }

        return this.childThis;
    }

    /**
     * 选择SQL字段
     *
     * @param field      SQL字段
     * @param fieldAlias 字段别名
     * @return 具体实现
     */
    @Override
    public C selectAs(String field, String fieldAlias) {
        if (StringUtils.isBlank(fieldAlias)) {
            throw new MybatisPlusException("选择字段["+field+"]的别名不能为空");
        }

        this.addSelectItem(
            new StandardSelectItem(
                field + " AS " + fieldAlias
            )
        );
        return this.childThis;
    }

    /**
     * 选择SQL字段
     *
     * @param field      SQL字段枚举
     * @param fieldAlias 字段别名
     * @return 具体实现
     */
    @Override
    public C selectAs(Enum<?> field, String fieldAlias) {
        return this.selectAs(
            this.resolveFieldName(field),
            fieldAlias
        );
    }

    /**
     * 选择SQL字段
     *
     * @param field      SQL字段枚举
     * @param fieldAlias 字段别名枚举
     * @return 具体实现
     */
    @Override
    public C selectAs(Enum<?> field, Enum<?> fieldAlias) {
        return this.selectAs(field, fieldAlias.name());
    }

    /**
     * 选择子查询结果
     *
     * @param sqlBuilderConsumer SQL构建器消费器
     * @param fieldAlias         字段别名
     * @return 具体实现
     */
    @Override
    public C selectAs(Consumer<StandardSelectSql> sqlBuilderConsumer, String fieldAlias) {
        StandardSelectSql standardSelectSql = new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(),
                this.getTableAliasManager()
            )
        );
        sqlBuilderConsumer.accept(standardSelectSql);
        this.addSelectItem(
            new SubQuerySelectItem(
                standardSelectSql.buildSqlFragment(),
                fieldAlias
            )
        );

        return this.childThis;
    }

    /**
     * 选择子查询结果
     *
     * @param sqlBuilderConsumer SQL构建器消费器
     * @param fieldAlias         字段别名枚举
     * @return 具体实现
     */
    @Override
    public C selectAs(Consumer<StandardSelectSql> sqlBuilderConsumer, Enum<?> fieldAlias) {
        return this.selectAs(sqlBuilderConsumer, fieldAlias.name());
    }

    /**
     * 选择SQL字段
     *
     * @param fields SQL字段
     * @return 具体实现
     */
    @Override
    public C select(Collection<String> fields) {
        if (CollectionUtils.isEmpty(fields)) {
            return this.childThis;
        }

        for (String field : fields) {
            this.selectItems.add(
                new StandardSelectItem(field)
            );
        }

        return this.childThis;
    }

    /**
     * 选择SQL字段表达式
     *
     * @param expressions SQL字段表达式
     * @return 具体实现
     */
    @Override
    public C selectExpression(Collection<String> expressions) {
        if (CollectionUtils.isEmpty(expressions)) {
            return this.childThis;
        }

        for (String expression : expressions) {
            this.addSelectItem(
                new ExpressionSelectItem(expression)
            );
        }

        return this.childThis;
    }

    /**
     * 选择SQL字段
     *
     * @param fields SQL字段枚举
     * @return 具体实现
     */
    @Override
    public C selectEnum(Collection<Enum<?>> fields) {
        if (CollectionUtils.isEmpty(fields)) {
            return this.childThis;
        }

        for (Enum<?> field : fields) {
            this.addSelectItem(
                new StandardSelectItem(
                    this.resolveFieldName(field)
                )
            );
        }

        return this.childThis;
    }

    /**
     * 添加选择项
     *
     * @param selectItem 选择项
     */
    @Override
    protected void addSelectItem(SelectItem selectItem) {
        this.selectItems.add(selectItem);
    }

    /**
     * 设置查询表
     *
     * @param entityClass 实体类
     * @param tableAlias  表别名
     * @return 具体实现
     */
    @Override
    public C from(Class<? extends BaseEntity> entityClass, String tableAlias) {
        this.fromItem = new StandardFromItem(
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias
        );

        this.getTableAliasManager().registerAlias(entityClass, tableAlias);
        return this.childThis;
    }

    /**
     * 设置查询表
     *
     * @param table      表名
     * @param tableAlias 表别名
     * @return 具体实现
     */
    @Override
    public C from(String table, String tableAlias) {
        this.fromItem = new StandardFromItem(
            table, tableAlias
        );

        this.getTableAliasManager().registerAlias(table, tableAlias);
        return this.childThis;
    }

    /**
     * 设置查询表
     *
     * @param entityClass 实体类
     * @return 具体实现
     */
    @Override
    public C from(Class<? extends BaseEntity> entityClass) {
        this.fromItem = new StandardFromItem(
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            TABLE_ALIAS
        );

        this.getTableAliasManager().registerAlias(entityClass, TABLE_ALIAS);
        return this.childThis;
    }

    /**
     * 设置查询表
     *
     * @param table 表名
     * @return 具体实现
     */
    @Override
    public C from(String table) {
        this.fromItem = new StandardFromItem(
            table,
            TABLE_ALIAS
        );

        this.getTableAliasManager().registerAlias(table, TABLE_ALIAS);
        return this.childThis;
    }

    /**
     * 设置子查询表
     *
     * @param sqlBuilderConsumer SQL构建器消费器
     * @param tableAlias         表别名
     * @return 具体实现
     */
    @Override
    public C from(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
        StandardSelectSql standardSelectSql = new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(),
                this.getTableAliasManager()
            )
        );
        sqlBuilderConsumer.accept(standardSelectSql);
        this.fromItem = new SubQueryFromItem(
            standardSelectSql.buildSqlFragment(),
            tableAlias
        );

        return this.childThis;
    }

    /**
     * 设置Where条件
     *
     * @return 条件构建器
     */
    @Override
    public SelectConditionBuilder<SelectConditionBuilderImpl, AbstractSelectCondition<SelectConditionBuilderImpl>.HavingConditionBuilderImpl, C> where() {
        if (this.selectConditionBuilder == null) {
            this.selectConditionBuilder = new SelectConditionBuilderImpl();
        }

        return this.selectConditionBuilder;
    }

    /**
     * 构建SQL之前处理
     */
    @Override
    protected void beforeBuild() {
        if (this.selectItems.isEmpty()) {
            this.selectItems.add(
                new CompositeSelectItem(
                    List.of(new StandardSelectItem("*"))
                )
            );
        } else {
            this.addSqlFragment(
                new CompositeSelectItem(
                    this.selectItems.stream()
                        .map(
                            selectItem -> {
                                if (selectItem instanceof StandardSelectItem standardSelectItem && StringUtils.isBlank(standardSelectItem.getTableAlias())) {
                                    return new StandardSelectItem(this.fromItem.getTableAlias(), standardSelectItem.getField(), standardSelectItem.getFieldAlias());
                                }

                                return selectItem;
                            }
                        )
                        .toList()
                )
            );
        }

        if (this.fromItem == null) {
            throw new MybatisPlusException( "SQL构建未指定【FROM】");
        }

        this.addSqlFragment(this.fromItem);
        super.beforeBuild();

        if (this.selectConditionBuilder != null) {
            this.addSqlFragment(this.selectConditionBuilder);
        }
    }

    /**
     * 查询条件构建器实现
     */
    public class SelectConditionBuilderImpl extends AbstractSelectCondition<SelectConditionBuilderImpl> implements SelectConditionBuilder<SelectConditionBuilderImpl, AbstractSelectCondition<SelectConditionBuilderImpl>.HavingConditionBuilderImpl, C> {

        /**
         * 获取SQL参数管理器
         *
         * @return SQL参数管理器
         */
        @Override
        public SqlParameterManager getSqlParameterManager() {
            return AbstractSelectSql.this.getSqlParameterManager();
        }

        /**
         * 返回上一级对象
         *
         * @return 上级对象
         */
        @Override
        public C end() {
            return AbstractSelectSql.this.childThis;
        }

        /**
         * 获取表别名管理器
         *
         * @return 表名别管理器
         */
        @Override
        public TableAliasManager getTableAliasManager() {
            return AbstractSelectSql.this.getTableAliasManager();
        }
    }
}
